from GameServer.models import History
from GameServer.config import *

from GameServer.redis_cache import RedisCache

import time

from threading import Lock

import config


def get_time():
    return int(time.time()) % 1e6


def get_history(username):
    user_history = History.objects.filter(username=username)[0]
    success = user_history.success
    failure = user_history.failure
    score = user_history.score
    return success, failure, score


class DictCache:
    def __init__(self):
        self.data = dict()
        self.lock = Lock()

    def get(self, key):
        self.lock.acquire()
        try:
            val = self.data.get(key)
        except Exception:
            raise
        finally:
            self.lock.release()
        return val

    def set(self, key, val):
        self.lock.acquire()
        try:
            self.data[key] = val
        except Exception:
            raise
        finally:
            self.lock.release()

    def pop(self, key):
        self.lock.acquire()
        try:
            self.data.pop(key)
        except Exception:
            raise
        finally:
            self.lock.release()

    def __iter__(self):
        for item in self.data:
            yield item


class UserCache:
    def __init__(self):
        print('init cache')
        # self.waiting_users = DictCache()  # {username: score}
        # self.partner_users = DictCache()  # {username: [partner_name, self_ready, partner_ready]}
        # self.playing_users = DictCache()  # {username: partner_name, step_num, role, action}
        # self.online_users = DictCache()   # {username: time}
        self.waiting_users = RedisCache('waiting')  # {username: score}
        self.partner_users = RedisCache('partner')  # {username: [partner_name, self_ready, partner_ready]}
        self.playing_users = RedisCache('playing')  # {username: partner_name, step_num, role, action}
        self.online_users = RedisCache('online')   # {username: time}

    def get_online_state(self, username):
        last_time = self.online_users.get(username)
        if last_time is None:
            return False
        if get_time() - float(last_time) > config.time_out:
            return False
        return True

    def update_last_time(self, username):
        self.online_users.set(username, get_time())

    def can_sign_in(self, username):
        last_time = self.online_users.get(username)
        if last_time is None:
            return True  # has not signed in
        if get_time() - float(last_time) > config.time_out:
            return True  # time out connect closed
        return False

    def do_sign_in(self, username):
        user_history = History.objects.filter(username=username)[0]
        score = user_history.score
        self.waiting_users.set(username, score)
        self.online_users.set(username, get_time())
        print('%s signed in' % username)

    def do_sign_out(self, username):
        if self.online_users.get(username):
            self.online_users.pop(username)
        if self.waiting_users.get(username):
            self.waiting_users.pop(username)
        elif self.partner_users.get(username):
            # set new partner for his partner
            self.do_change_partner(username)
            self.partner_users.pop(username)
        elif self.playing_users.get(username):
            self.playing_users.pop(username)
        print('%s signed out' % username)

    def do_change_partner(self, username):
        # get new partner
        # pop waiting users
        # set them as each partner
        state = self.partner_users.get(username)
        partner = state[0]
        for name in self.waiting_users:
            if name != partner:
                # check online
                if not self.get_online_state(name):
                    self.do_sign_out(name)
                    continue
                # pop waiting users
                self.waiting_users.pop(name)
                # set new partner
                self.partner_users.set(username, [name, 0, 0])
                self.partner_users.set(name, [username, 0, 0])
                # clear ex-partner
                # self.partner_users.pop(partner)
                self.partner_users.set(partner, [None, 0, 0])
                success, failure, score = get_history(partner)
                self.waiting_users.set(partner, score)
                return name
        return None

    def do_first_partner(self, username):
        # get partner name
        # pop from waiting users
        # set them as each partner
        for name in self.waiting_users:
            if name != username:
                if not self.get_online_state(name):
                    self.do_sign_out(name)
                    continue
                self.waiting_users.pop(name)
                self.partner_users.set(username, [name, 0, 0])
                self.partner_users.set(name, [username, 0, 0])
                return name

    def add_action(self, username, action):
        state = self.playing_users.get(username)
        state[3].append(action)
        self.playing_users.set(username, state)
        print(username + ' actions updated:')
        print(self.playing_users.get(username)[3])

    def get_action(self, username):
        state = self.playing_users.get(username)
        actions = state[3]
        if actions.__len__() == 0:
            action = {
                'action_type': 'nothing',
            }
        else:
            action = actions[0]
            actions.pop(0)
            state[3] = actions
            self.playing_users.set(username, state)
        print(username + ' get action:')
        print(action)
        return action

    def do_start_game(self, username):
        partner = self.partner_users.get(username)[0]
        self.partner_users.pop(username)
        if self.partner_users.get(partner):
            role = 1  # first hand
        else:
            role = -1  # second hand
        self.playing_users.set(username, [partner, 0, role, []])
        return role

    def do_new_step(self, username, x, y):
        # update step_num
        self_state = self.playing_users.get(username)
        self_state[1] += 1
        self.playing_users.set(username, self_state)
        # add action
        partner = self_state[0]
        action = {
            'action_type': 'new_step',
            'x': x,
            'y': y
        }
        self.add_action(partner, action)

    def do_end_game(self, username):
        state = self.playing_users.get(username)
        if state is None:
            return
        partner = state[0]
        self.playing_users.pop(username)
        self.partner_users.set(username, [partner, 0, 0])
        # if self.playing_users.get(partner):
        #     state = self.playing_users.get(partner)
        #     state[0] = None
        #     self.playing_users.set(partner, state)

    def do_resign(self, username):
        partner = self.playing_users.get(username)[0]
        action = {
            'action_type': 'partner_resign'
        }
        self.add_action(partner, action)

    def do_retract_ask(self, username):
        action = {
            'action_type': 'partner_retract_ask'
        }
        partner = self.playing_users.get(username)[0]
        self.add_action(partner, action)

    def do_retract_answer(self, username, permission):
        action = {
            'action_type': 'retract_answer',
            'permission': permission
        }
        partner = self.playing_users.get(username)[0]
        self.add_action(partner, action)

    def do_peace_ask(self, username):
        action = {
            'action_type': 'partner_peace_ask'
        }
        partner = self.playing_users.get(username)[0]
        self.add_action(partner, action)

    def do_peace_answer(self, username, permission):
        action = {
            'action_type': 'peace_answer',
            'permission': permission
        }
        partner = self.playing_users.get(username)[0]
        self.add_action(partner, action)

cache = UserCache()
